在前幾篇介紹表單的文章中,有說到表單的基本操作以及如何將資料送到後端
但是問題來了:誰來掌握表單輸入的資料?
舉個更貼近實際的例子:
我們需要登入一個帳戶,使用者需要輸入帳號密碼,但 React 要怎麼得知使用者輸入的值:
state 記錄第一種方式就是受控表單 (Controlled),第二種方式則是非受控表單 (Uncontrolled)
兩者主要的差別就在於:輸入值是由 React 控制,還是由 DOM 控制
React 強調「UI = State」,也就是畫面應該由 state 決定,而不是直接讓 DOM 管理值。
受控表單正是依循這個理念:
value 綁定 state:輸入顯示的值直接來自 React state
onChange 更新 state:每次使用者輸入時,React 都更新 state
state 掌控state 控制,使用 value + onChange
state 更新 ,且輸入值或跟著同步import { useState } from "react";
export default function Contact() {
  const [email, setEmail] = useState("");
  const handleSubmit = (e) => {
    e.preventDefault();
    alert("受控送出的電子郵件:" + email);
  };
  return (
    <div style={{ border: "1px solid #ddd", padding: "1rem", borderRadius: "8px" }}>
      <h3>受控表單 (Controlled)</h3>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={email}
          placeholder="輸入電子郵件"
          onChange={(e) => setEmail(e.target.value)}
        />
        <button type="submit">送出</button>
      </form>
    </div>
  );
}
ref
import { useRef } from "react";
export default function Contact() {
  const emailRef = useRef();
  const handleSubmit = (e) => {
    e.preventDefault();
    alert("非受控送出的電子郵件:" + emailRef.current.value);
  };
  return (
    <div>
      <h3>非受控表單 (Uncontrolled)</h3>
      <form onSubmit={handleSubmit}>
        <input type="text" ref={emailRef} placeholder="輸入電子郵件" />
        <button type="submit">送出</button>
      </form>
    </div>
  );
}
註:useRef 的功能:
<input>、<textarea> 等元素,讀取或操作它們的值useRef 的 .current 屬性可以存放任何值(例如數字、物件、函式...)import { useState } from "react";
export default function Contact() {
  const [message, setMessage] = useState("");
  const handleSubmit = (e) => {
    e.preventDefault();
    alert("送出的訊息:" + message);
  };
  return (
    <form onSubmit={handleSubmit}>
      <textarea
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        placeholder="輸入訊息"
      />
      <p style={{ fontSize: "0.9rem", color: "#555" }}>
        已輸入字數:{message.length}
      </p>
      <button type="submit">送出</button>
    </form>
  );
}
瀏覽器執行畫面
| 受控 (Controlled) | 非受控 (Uncontrolled) | |
|---|---|---|
| 資料來源 | React state | DOM | 
| 操作方式 | value + onChange | 
ref | 
| 適合情境 | 需要驗證、即時反應、動態顯示 | 表單簡單、不需要驗證 | 
| 優點 | 容易整合驗證邏輯、單一資料流 | 程式碼簡單 | 
| 缺點 | 需要大量 state、程式碼較冗長 | 難以追蹤輸入變化 | 
從上表可以看到,受控表單適合需要即時驗證或動態顯示的複雜情境,非受控表單則適合簡單情境或只在送出時取得值。